// Since this must be included before #include<windows.h>
#if defined(WIN32)
#   include <Winsock2.h>
#   include <Ws2tcpip.h>
#endif

#include "iConfig.hpp"

#if GLF_ENABLE_DEBUGGER

#include <glf/debugger/netInterface.h>
#include <glf/core/timer.h>

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#if defined(GLF_PLATFORM_PS3)
#include <sysutil/sysutil_common.h>
#include <sys/time.h>
#endif

#include <glf/debug/console.h>
#define GLF_NETINTEFRFACE_ERROR_PRINT(...)  Console::PushColor(Console::COLOR_RED);Console::Print("netinterface error: ");Console::PopColor(); Console::Println(__VA_ARGS__)
#ifdef GLF_NETINTEFRFACE_VERBOSE
#	define GLF_NETINTEFRFACE_VERBOSE_PRINT(...) Console::Print("netinterface: "); Console::Println(__VA_ARGS__)
#else
#	define GLF_NETINTEFRFACE_VERBOSE_PRINT(...)
#endif

#if defined(GLF_PLATFORM_CTR)
#include <nn/uds.h>
#include <nn/socket.h>

int strcpy( char* dst,const char*  src,long size)
{
	while ( size > 1 && *src != 0 )
	{
		*dst++ = *src++;
		size--;
	}

	*dst = 0;  /* always zero-terminate */

	return *src != 0;
}
#endif

namespace glf
{
namespace debugger
{

CNetSocket::CNetSocket(SOCKET sock)
:
Sock(sock),
Error(0)
{
}

CNetSocket::~CNetSocket()
{
	::closesocket(Sock);
}

int CNetSocket::getError() const
{
	return Error;
}

bool CNetSocket::isOpen() const
{
	return getError() == 0;
}

bool CNetSocket::isReadable() const
// Return true if this socket has incoming data available.
{
#if defined(GLF_PLATFORM_CTR)
	s32 readVal;
	const int LENGTH = 1;
	static char buffer[LENGTH];
	readVal = nn::socket::Recv(Sock, buffer, LENGTH, nn::socket::MSG_PEEK|nn::socket::MSG_DONTWAIT); //MSG_PEEK ==data is received by the state is not changed, and the same data can be received again

	if (readVal==nn::socket::EAGAIN)
		return false;

	if (readVal>0)
	{
//		GLF_NETINTEFRFACE_VERBOSE_PRINT("Received messages %d bytes",readVal);
	}
	else
		switch(readVal)
		{
		case 0:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("For stream sockets (SOCK_STREAM), this indicates that the socket cannot accept any more messages because the remote host has finished sending messages. For datagram sockets (SOCK_DGRAM), this function does not return 0.");
			break;
		case nn::socket::ENETRESET:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
			break;
		case nn::socket::EBADF:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid socket descriptor.");
			break;
		case nn::socket::EAGAIN:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("There is no data or out-of-band data waiting to be received, even when O_NONBLOCK is set in the socket descriptor (or when the MSG_DONTWAIT flag is set).");
            return false;
		case nn::socket::EINVAL:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid processing.");
			break;
		case nn::socket::EOPNOTSUPP:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Process not supported.");
			break;
		case nn::socket::ENOTCONN:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Not connected.");
			break;
		case nn::socket::ECONNRESET:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("The connection was reset.");
			readVal=1;
			break;
		case nn::socket::EINTR:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Suspended.");
			break;
		case nn::socket::ETIMEDOUT:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Timeout.");
			break;
		case nn::socket::ENETDOWN:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("The local network interface is down.");
			break;
		};

	return readVal > 0;
#else
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(Sock, &fds);
	struct timeval tv = { 0, 0 };

	#ifdef WIN32

		// the first arg to select in win32 is ignored.
		// It's included only for compatibility with Berkeley sockets.
		int ret = select(1, &fds, NULL, NULL, &tv);

	#elif defined(GLF_PLATFORM_PS3)
		
		int ret = socketselect(Sock + 1, &fds, NULL, NULL, &tv);

	#elif defined(GLF_PLATFORM_PSP2)
		
		char b;
		const int MINIMUM_SIZE = 16;
		int ret = sceNetRecv(Sock, &b, MINIMUM_SIZE, SCE_NET_MSG_PEEK | SCE_NET_MSG_DONTWAIT);
		
        // HACK : This sleep prevent a crash inside libscenet in release builds...
        sceKernelDelayThread(1); // sleep 1 usec

		if(ret < 0 && ret != SCE_NET_ERROR_EAGAIN)
		{
			Error = WSAGetLastError();
			return false;
		}
		return ret >= MINIMUM_SIZE;
		
	#else
		
		// It should be the value of the highest numbered FD within any of the passed fd_sets,
		// plus one... Because, the max FD value + 1 == the number of FDs
		// that select() must concern itself with, from within the passed fd_sets...)
		int ret = select(Sock + 1, &fds, NULL, NULL, &tv);

	#endif

		if(ret == 0)
		{
			return false;
		}
		else if(ret < 0)
		{
			Error = WSAGetLastError();
			return false;
		}

		if(FD_ISSET(Sock, &fds))
		{
			// Socket has data available.
			return true;
		}

	return false;
#endif
}

int CNetSocket::read(void* data, int bytes, float timeout_seconds)
// Try to read the requested number of bytes.  Returns the
// number of bytes actually read.
{
	int total_bytes_read = 0;

	for(;;)
	{
		int64 start = GetMilliseconds();

		GLF_NETINTEFRFACE_VERBOSE_PRINT("recv %d bytes from socket %d...\n", bytes, Sock);
		int bytes_read = 0;
#if defined(GLF_PLATFORM_CTR)		
		bytes_read=nn::socket::Recv(Sock, (char*) data, bytes, 0);

		if (bytes_read>0)
		{
//			GLF_NETINTEFRFACE_VERBOSE_PRINT("Received messages %d bytes",bytes_read);
		}
		else
			switch(bytes_read)
		{
			case 0:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("For stream sockets (SOCK_STREAM), this indicates that the socket cannot accept any more messages because the remote host has finished sending messages. For datagram sockets (SOCK_DGRAM), this function does not return 0.");
				break;
			case nn::socket::ENETRESET:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
				break;
			case nn::socket::EBADF:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid socket descriptor.");
				break;
			case nn::socket::EAGAIN:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("There is no data or out-of-band data waiting to be received, even when O_NONBLOCK is set in the socket descriptor (or when the MSG_DONTWAIT flag is set).");
				break;
			case nn::socket::EINVAL:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid processing.");
				break;
			case nn::socket::EOPNOTSUPP:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Process not supported.");
				break;
			case nn::socket::ENOTCONN:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Not connected.");
				break;
			case nn::socket::ECONNRESET:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("The connection was reset.");
				break;
			case nn::socket::EINTR:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Suspended.");
				break;
			case nn::socket::ETIMEDOUT:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("Timeout.");
				break;
			case nn::socket::ENETDOWN:
				GLF_NETINTEFRFACE_VERBOSE_PRINT("The local network interface is down.");
				break;
		};



#else
		bytes_read= recv(Sock, (char*) data, bytes, 0);
#endif


		GLF_NETINTEFRFACE_VERBOSE_PRINT("bytes_read=%d bytes\n", bytes_read);

		if(bytes_read == 0)
		{
			break;
		}

		if(bytes_read == SOCKET_ERROR || bytes_read < 0)
		{
#if defined(GLF_PLATFORM_CTR)
			if(bytes_read == nn::socket::EAGAIN)
#else
			Error = WSAGetLastError();
			if(Error == WSAEWOULDBLOCK)
#endif
			{
				// No data ready.
				Error = 0;

				bool readable = isReadable();
				if(!readable && Error != 0)
				{
					return total_bytes_read;
				}

				// Timeout?
				int64 now = GetMilliseconds();
				double elapsed = (now - start) / 1000.0f;

				if(elapsed < timeout_seconds)
				{
#ifdef _WIN32
					// Sleep(0);
#elif defined(GLF_PLATFORM_CTR)	
					nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(10));
#elif defined(GLF_PLATFORM_PS3)
					cellSysutilCheckCallback();
#elif defined(GLF_PLATFORM_PSP2)
					Thread::Sleep(1);
#else
					sleep(1);
#endif

					// TODO this spins; fix it by sleeping a bit?
					// if(time_left > 0.010f) { sleep(...); }

					continue;
				}

				// Timed out.
				return total_bytes_read;
			}
			else
			{
#if defined(GLF_PLATFORM_CTR)
					// Presumably something bad.
					GLF_NETINTEFRFACE_ERROR_PRINT("CNetSocket::read() error in recv, error code = %d\n",bytes_read);	
#else
					// Presumably something bad.
					GLF_NETINTEFRFACE_ERROR_PRINT("CNetSocket::read() error in recv, error code = %d\n",WSAGetLastError());
#endif
					return total_bytes_read;
			}
		}

		// Got some data.
		total_bytes_read += bytes_read;
		bytes -= bytes_read;
		data = (void*) (((char*) data) + bytes_read);

		//assert(bytes >= 0);
		if(bytes < 0)
		{
			// ?!?!?!?! no effect!?! (warning with PS3)
			//printf("");
		}

		if(bytes == 0)
		{
			// Done.
			break;
		}
	}

	return total_bytes_read;
}

int CNetSocket::write(const void* data, int bytes, float timeout_seconds)
// Try to write the given data buffer.  Return the number of
// bytes actually written.
{
	int64 start = GetMilliseconds();

	int total_bytes_written = 0;

	for(;;)
	{
		int bytes_sent=0;
#if defined(GLF_PLATFORM_CTR)
		int result = 0;
		result ==nn::socket::Send(Sock, (const char*) data, bytes, 0);


		if (result>0)
		{
//			GLF_NETINTEFRFACE_VERBOSE_PRINT("Send messages %d bytes",bytes_sent);
		}
		else
			switch(result)
			{
				case 0:
					bytes_sent=bytes;
//					GLF_NETINTEFRFACE_VERBOSE_PRINT("Send ok.");
					break;
				case nn::socket::ENETRESET:
					GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
					break;
				case nn::socket::EBADF:
					GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid socket descriptor.");
					break;
			};

#else
		bytes_sent =send(Sock, (const char*) data, bytes, 0);
#endif
		if(bytes_sent == SOCKET_ERROR || bytes_sent < 0)
		{
#if defined(GLF_PLATFORM_CTR)
			// nothing to do here for 3DS.
			// the send method is blocking until data payload has been sent
			// we shouldn't not get that kind of error below
#else
			Error = WSAGetLastError();
			if(Error == WSAENOBUFS || Error == WSAEWOULDBLOCK)
#endif
			{
				// Non-fatal.
				Error = 0;

				int64 now = GetMilliseconds();
				double elapsed = (now - start) / 1000.0f;

				if(elapsed < timeout_seconds)
				{
#if GLF_PLATFORM_PS3
					sys_timer_usleep(10);
					cellSysutilCheckCallback();
#elif defined(GLF_PLATFORM_PSP2)
					Thread::Sleep(10);
#endif

					// Keep trying.
					// TODO possibly sleep() here
					continue;
				}

				// Timed out.
				return total_bytes_written;
			}

#if !defined(GLF_PLATFORM_CTR)
			// write error
			return total_bytes_written;
#endif
		}

		total_bytes_written += bytes_sent;
		data = (const void*) (((const char*) data) + bytes_sent);
		bytes -= bytes_sent;

		assert(bytes >= 0);
		if(bytes == 0)
		{
			// Done.
			return total_bytes_written;
		}
		// else keep trying.
	}
}

void CNetSocket::setNonBlock(bool nonBlock)
{
	GLF_NETINTEFRFACE_VERBOSE_PRINT("setNonBlock %d\n", nonBlock);

#ifdef _WIN32
	int mode = nonBlock ? 1 : 0;
	ioctlsocket(Sock, FIONBIO, (u_long FAR*) &mode);
#elif defined(GLF_PLATFORM_PS3)
	int optval = nonBlock ? 1 : 0;
	setsockopt(Sock, SOL_SOCKET, SO_NBIO, &optval, sizeof(optval));
#elif defined(GLF_PLATFORM_CTR)
	int mode = nn::socket::Fcntl(Sock, nn::socket::F_GETFL, 0);
	if(nonBlock)
		mode |= nn::socket::O_NONBLOCK;
	else
		mode &= ~nn::socket::O_NONBLOCK;
	nn::socket::Fcntl(Sock, nn::socket::F_SETFL, mode);
#elif defined(GLF_PLATFORM_PSP2)
	int optval = nonBlock ? 1 : 0;
	sceNetSetsockopt(Sock, SCE_NET_SOL_SOCKET, SCE_NET_SO_NBIO, &optval, sizeof(optval));
#else
	int mode = fcntl(Sock, F_GETFL, 0);
	if(nonBlock)
		mode |= O_NONBLOCK;
	else
		mode &= ~O_NONBLOCK;
	fcntl(Sock, F_SETFL, mode);
#endif

#if !defined(_WIN32) && !defined(GLF_PLATFORM_CTR) && !defined(GLF_PLATFORM_PSP2)
	if(!nonBlock)
	{
		timeval optval;
		optval.tv_sec = 5;
		optval.tv_usec = 0;
		int ret = setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, &optval, sizeof(optval));
		if(ret != 0)
		{
			Console::Println("error setting SO_RCVTIMEO %d\n", WSAGetLastError());
		}
	}
#endif
}

void CNetInterface::setNonBlock()
{
#ifdef _WIN32
	int mode = 1;
	ioctlsocket(mSocket, FIONBIO, (u_long FAR*) &mode);
#elif defined(GLF_PLATFORM_PS3)
	int optval = 1;
	setsockopt(mSocket, SOL_SOCKET, SO_NBIO, &optval, sizeof(optval));
#elif defined(GLF_PLATFORM_CTR)
	int mode = nn::socket::Fcntl(mSocket, nn::socket::F_GETFL, 0);
	mode |= nn::socket::O_NONBLOCK;
	nn::socket::Fcntl(mSocket, nn::socket::F_SETFL, mode);
#elif defined(GLF_PLATFORM_PSP2)
	int optval = 1;
	sceNetSetsockopt(mSocket, SCE_NET_SOL_SOCKET, SCE_NET_SO_NBIO, &optval, sizeof(optval));
#else
	int mode = fcntl(mSocket, F_GETFL, 0);
	mode |= O_NONBLOCK;
	fcntl(mSocket, F_SETFL, mode);
#endif
}

// client
CNetInterface::CNetInterface() :
mSocket(INVALID_SOCKET)
{
}

bool CNetInterface::listen(int port)
{
	mPortNumber = port;

#if defined(GLF_PLATFORM_CTR)

	// not sure about 0 here
	mSocket = nn::socket::Socket(nn::socket::AF_INET, nn::socket::SOCK_STREAM, 0);

	if (mSocket>=1)
    {
		GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket ok=>%d",mSocket);
    }
    else{
		switch(mSocket)
		{
		case nn::socket::ENETRESET:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
			break;
		case nn::socket::EAFNOSUPPORT:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Address family not supported.");
			break;
		case nn::socket::EPROTONOSUPPORT:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Protocol not supported.");
			break;
		case nn::socket::EMFILE:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Cannot create any more socket descriptors.");
			break;
		case nn::socket::ENOMEM:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Insufficient memory.");
			break;
		case nn::socket::EPROTOTYPE:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket type not supported.");
 			break;
		};
    }
#elif defined(GLF_PLATFORM_PSP2)
	mSocket = sceNetSocket("CNetInterfaceListen", SCE_NET_AF_INET, SCE_NET_SOCK_STREAM, 0);
#else
	mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif

	if(mSocket == INVALID_SOCKET || (int)mSocket < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("can't open listen socket\n");
		mSocket = INVALID_SOCKET;
		return false;
	}

	// Set the address.
	SOCKADDR_IN saddr;
#if defined(GLF_PLATFORM_CTR)
	saddr.family = nn::socket::AF_INET;
	saddr.addr.addr = INADDR_ANY;
	saddr.port = nn::socket::HtoNs(mPortNumber);
	saddr.len = sizeof(nn::socket::SockAddrIn);
#elif defined(GLF_PLATFORM_PSP2)
	memset(&saddr, 0, sizeof(SOCKADDR_IN));
	saddr.sin_family = SCE_NET_AF_INET;
	saddr.sin_addr.s_addr = 0;
	saddr.sin_port = htons(mPortNumber);
#else
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = INADDR_ANY;
	saddr.sin_port = htons(mPortNumber);
#endif
	// bind the address
#if defined(GLF_PLATFORM_CTR)
	int ret = nn::socket::Bind(mSocket, &saddr);
	if (ret==0)
    {
		GLF_NETINTEFRFACE_VERBOSE_PRINT("Bind ok");
    }
	else
    {
		switch(ret)
		{
		case 0:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Process was successful.");
			break;
		case nn::socket::ENETRESET:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
			break;
		case nn::socket::EBADF:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid socket descriptor.");
			break;
		case nn::socket::EOPNOTSUPP:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Process not supported.");
			break;
		case nn::socket::EINVAL:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid processing. (Socket is already bound to address, etc.)");
			break;
		case nn::socket::EAFNOSUPPORT:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Address family not supported.");
			break;
		case nn::socket::EADDRINUSE:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Address is already in use.");
			break;
		};
    }
#else
	int ret = bind(mSocket, (LPSOCKADDR) &saddr, sizeof(saddr));
#endif

#if defined(GLF_PLATFORM_CTR)
	if(ret  < 0)
#else
	if(ret == SOCKET_ERROR)
#endif
	{
		//GLF_NETINTEFRFACE_ERROR_PRINT("bind failed\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}

	// gethostname

	// Start listening.
#if defined(GLF_PLATFORM_CTR)
	ret = nn::socket::Listen(mSocket, 1);
	if (ret==0)
    {
		GLF_NETINTEFRFACE_VERBOSE_PRINT("Listen ok.");
    }
    else
    {
		switch(ret)
		{
		case 0:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Process was successful.");
			break;
		case nn::socket::ENETRESET:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
			break;
		case nn::socket::EBADF:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid socket descriptor.");
			break;
		case nn::socket::EOPNOTSUPP:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Process not supported.");
			break;
		case nn::socket::EINVAL:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid processing.");
			break;
		case nn::socket::ENOBUFS:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("o");
			break;
		};
    }

#else
	ret = ::listen(mSocket, SOMAXCONN);

#endif

#if defined(GLF_PLATFORM_CTR)
	if(ret != 0)
#else
	if(ret == SOCKET_ERROR)
#endif
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("listen() failed\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}

	// Set non-blocking mode for the socket, so that
	// accept() doesn't block if there's no pending
	// connection.
	setNonBlock();
	return true;
}

CNetSocket* CNetInterface::accept()
{
	// Accept an incoming request.
	SOCKET	remote_socket;

#if defined(GLF_PLATFORM_CTR)

	nn::socket::SockAddrIn l_SockAddrIn;

	remote_socket = nn::socket::Accept(mSocket, &l_SockAddrIn);

	if (remote_socket>=1) {
		GLF_NETINTEFRFACE_VERBOSE_PRINT("Accept ok");
    } else {
		switch(remote_socket)
		{
		case nn::socket::EINVAL:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid processing.");
			break;
		case nn::socket::ENETRESET:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Socket library is not initialized.");
			break;
		case nn::socket::EBADF:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Invalid socket descriptor.");
			break;
		case nn::socket::EOPNOTSUPP:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Process not supported.");
			break;
		case nn::socket::ENOBUFS:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Insufficient resources.");
			break;
		case nn::socket::EAGAIN:
			//GLF_NETINTEFRFACE_VERBOSE_PRINT("O_NONBLOCK is set in the socket descriptor and there is no connection currently available for acceptance.");
			break;
		case nn::socket::ECONNABORTED:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("The connection was cancelled.");
			break;
		case nn::socket::ENOMEM:
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Insufficient memory.");
			break;
		}
    }

#else
	remote_socket = ::accept(mSocket, NULL, NULL);
#endif

	if((remote_socket == INVALID_SOCKET) || ((int)remote_socket < 0))
	{
		// No connection pending, or some other error.
		return NULL;
	}

	return new CNetSocket(remote_socket);
}

bool CNetInterface::setupReceiveMulticast(const char* group, int port)
{
	assert(group);
	assert(port > 0);

#if defined(GLF_PLATFORM_CTR)
	mSocket = nn::socket::Socket(nn::socket::AF_INET, nn::socket::SOCK_DGRAM, 0);
#elif defined(GLF_PLATFORM_PSP2)
	mSocket = sceNetSocket("CNetInterfaceListen", SCE_NET_AF_INET, SCE_NET_SOCK_DGRAM, 0);
#else
	mSocket = socket(AF_INET, SOCK_DGRAM, 0);
#endif

#if defined(GLF_PLATFORM_CTR)
	if(mSocket < 1)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("can't create datagram socket %d\n", mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}
#else
	if(mSocket == INVALID_SOCKET || (int)mSocket < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("can't create datagram socket %d\n", WSAGetLastError());
		mSocket = INVALID_SOCKET;
		return false;
	}
#endif

#ifdef WIN32
	char val = 1;
	if(setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("can't reuse address\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}
#endif

	// Set the address.
	SOCKADDR_IN saddr;
#if defined(GLF_PLATFORM_CTR)
	saddr.family = nn::socket::AF_INET;
	saddr.addr.addr = INADDR_ANY;
	saddr.port = nn::socket::HtoNs(port);
	saddr.len = sizeof(nn::socket::SockAddrIn);
	
	// bind the address
	int ret = nn::socket::Bind(mSocket, &saddr);
	if(ret < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("bind failed\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}
	nn::socket::IpMreq mreq;
	nn::socket::InAddr iaddr;
	nn::socket::InetAtoN(group,  &iaddr);
	
	mreq.multiaddr.addr = iaddr.addr;
	mreq.interface.addr = INADDR_ANY;
#elif defined(GLF_PLATFORM_PSP2)
	char ip[32] = {0};
	getHostAddress(ip, sizeof(ip));

	memset(&saddr, 0, sizeof(SceNetSockaddrIn));
	saddr.sin_family = SCE_NET_AF_INET;
	//sceNetInetPton(SCE_NET_AF_INET, ip, &saddr.sin_addr.s_addr);
	saddr.sin_addr.s_addr = 0;
	saddr.sin_port = htons(port);
	saddr.sin_len = sizeof(SceNetSockaddrIn);

	// bind the address
	int ret = bind(mSocket, (LPSOCKADDR) &saddr, sizeof(saddr));
	if(ret == SOCKET_ERROR || ret < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("bind failed\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}

	SceNetIpMreq mreq;
	sceNetInetPton(SCE_NET_AF_INET, group, &mreq.imr_multiaddr);
	//sceNetInetPton(SCE_NET_AF_INET, ip, &mreq.imr_interface.s_addr);
	mreq.imr_interface.s_addr = 0;
#else
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = INADDR_ANY;
	saddr.sin_port = htons(port);

	// bind the address
	int ret = bind(mSocket, (LPSOCKADDR) &saddr, sizeof(saddr));
	if(ret == SOCKET_ERROR || ret < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("bind failed\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}

	ip_mreq mreq;
	mreq.imr_multiaddr.s_addr = inet_addr(group);
	mreq.imr_interface.s_addr = INADDR_ANY;
#endif

#if defined(GLF_PLATFORM_CTR)
	if(nn::socket::SetSockOpt(mSocket, nn::socket::SOL_IP, nn::socket::IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
#elif defined(GLF_PLATFORM_PSP2)
	if(sceNetSetsockopt(mSocket, SCE_NET_IPPROTO_IP, SCE_NET_IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
#else
	if(setsockopt(mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) < 0)
#endif
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("can't add membership\n");
		::closesocket(mSocket);
		mSocket = INVALID_SOCKET;
		return false;
	}

	//struct in_addr interface_addr;
	//if(setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_IF, (const char*)&interface_addr, sizeof(interface_addr)) < 0)
	//{
	//	GLF_NETINTEFRFACE_ERROR_PRINT("can't use multicast\n");
	//	closesocket(mSocket);
	//	mSocket = INVALID_SOCKET;
	//	return false;
	//}

	mGroup = group;
	mPortNumber = port;
	setNonBlock();
	return true;
}

// http://tldp.org/HOWTO/Multicast-HOWTO-6.html
// http://www.sockets.com/ch16.htm#Multicast

int CNetInterface::receiveMulticast(void* data, int size)
{
	if(!isValid())
		return false;

	// Set the destination address.
	SOCKADDR_IN daddr;
#if defined(GLF_PLATFORM_CTR)
	nn::socket::InAddr iaddr;
	nn::socket::InetAtoN(mGroup.c_str(),  &iaddr);

	daddr.family = nn::socket::AF_INET;
	daddr.addr.addr = iaddr.addr;
	daddr.port = nn::socket::HtoNs(mPortNumber);
	daddr.len = sizeof(nn::socket::SockAddrIn);	
	int ret = nn::socket::RecvFrom(mSocket, (char*)data, size, 0, &daddr);
#elif defined(GLF_PLATFORM_PSP2)
	daddr.sin_family = SCE_NET_AF_INET;
	sceNetInetPton(SCE_NET_AF_INET, mGroup.c_str(), &daddr.sin_addr.s_addr);
	daddr.sin_port = htons(mPortNumber);

	SceNetSocklen_t addresslen = sizeof(SceNetSockaddr);
	int ret = recvfrom(mSocket, (char*)data, size, 0,(SceNetSockaddr*) &daddr, &addresslen);
#else
	daddr.sin_family = AF_INET;
	daddr.sin_addr.s_addr = inet_addr(mGroup.c_str());
	daddr.sin_port = htons(mPortNumber);

	socklen_t addresslen = sizeof(struct sockaddr);
	int ret = recvfrom(mSocket, (char*)data, size, 0,(struct sockaddr *) &daddr, &addresslen);
#endif
	return ret;
}

bool CNetInterface::sendDatagram(const char* address, int port, void* data, int size)
{
	if(!isValid())
		return false;

	// Set the destination address.
	SOCKADDR_IN daddr;
#if defined(GLF_PLATFORM_CTR)
	nn::socket::InAddr iaddr;
	nn::socket::InetAtoN(address,  &iaddr);

	daddr.family = nn::socket::AF_INET;
	daddr.addr.addr = iaddr.addr;
	daddr.port = nn::socket::HtoNs(port);
	daddr.len = sizeof(nn::socket::SockAddrIn);
	int ret = nn::socket::SendTo(mSocket, data, size, 0,&daddr);
#elif defined(GLF_PLATFORM_PSP2)
	memset(&daddr, 0, sizeof(daddr));
	daddr.sin_len = sizeof(daddr);
	daddr.sin_family = SCE_NET_AF_INET;
	sceNetInetPton(SCE_NET_AF_INET, address, &daddr.sin_addr.s_addr);
	daddr.sin_port = htons(port);

	int ret = sceNetSendto(mSocket, (char*)data, size, 0, (SceNetSockaddr*) &daddr, sizeof(daddr));
#else
	daddr.sin_family = AF_INET;
	daddr.sin_addr.s_addr = inet_addr(address);
	daddr.sin_port = htons(port);

	int ret = sendto(mSocket, (const char*)data, size, 0, 
					(struct sockaddr *) &daddr, 
					sizeof(struct sockaddr));
#endif
	return ret > 0;
}

bool CNetInterface::getHostName(char* buffer, int len)
{
#if GLF_PLATFORM_PS3
	return getHostAddress(buffer, len);
#elif defined(GLF_PLATFORM_CTR)
	nn::socket::InAddr addr, netmask;
	if (nn::socket::GetPrimaryAddress(reinterpret_cast<u8*>(&addr), reinterpret_cast<u8*>(&netmask))<0)
		return false;
	strcpy(buffer,nn::socket::InetNtoA(addr),len);
	buffer[len]='\0';
	return true;
#elif defined(GLF_PLATFORM_PSP2)
	return getHostAddress(buffer, len);
#else
	gethostname(buffer, len);
	return true;
#endif
}

bool CNetInterface::getHostAddress(char* buffer, int len) const
{
#if GLF_PLATFORM_PS3
	in_addr addr;
	addr.s_addr = 0;

	CellNetCtlInfo info;
	int ret = cellNetCtlGetInfo(CELL_NET_CTL_INFO_IP_ADDRESS, &info);
	if(ret < 0)
	{
		Console::Println("cellNetCtlGetInfo() failed (ret=0x%x)\n", ret);
		return false;
	}

	strcpy(buffer, info.ip_address);
	return true;
#elif defined(GLF_PLATFORM_CTR)
	nn::socket::InAddr addr, netmask;
	if (nn::socket::GetPrimaryAddress(reinterpret_cast<u8*>(&addr), reinterpret_cast<u8*>(&netmask))<0)
		return false;
	strcpy(buffer,nn::socket::InetNtoA(addr),len);
	buffer[len]='\0';
	return true;
#elif defined(GLF_PLATFORM_PSP2)
	SceNetCtlInfo info;
	int ret = sceNetCtlInetGetInfo(SCE_NET_CTL_INFO_IP_ADDRESS, &info);
	if(ret < 0)
		return false;
	strcpy(buffer, info.ip_address);
	return false;
#else
	if(gethostname(buffer, len) == 0)
	{
		struct hostent* hostinfo = gethostbyname(buffer);
		if(hostinfo != NULL)
		{
			const char* ip = inet_ntoa(*(struct in_addr *)*hostinfo->h_addr_list);
			strcpy(buffer, ip);
			return true;
		}
	}
	return false;
#endif
}

CNetInterface::~CNetInterface()
{
	close();
}

void CNetInterface::close()
{
	::closesocket(mSocket);
	mSocket = INVALID_SOCKET;
}

bool CNetInterface::isValid() const
{
	if(mSocket == INVALID_SOCKET)
	{
		return false;
	}

	return true;
}

#if defined(GLF_PLATFORM_CTR)
bool initCTRDebug()
{ 
    //not thread safe at all !!
	static bool m_ctrSocketLibIsInit = false;
	if (m_ctrSocketLibIsInit)
		return true;

	nn::fs::Initialize();
	nn::ac::Initialize();
	nn::Result result;
	nn::ac::Config config;

#if !defined(GLF_CTR_DEBUG_WIRELESS_SSID) \
    || !defined(GLF_CTR_DEBUG_WIRELESS_ENC) \
    || !defined(GLF_CTR_DEBUG_WIRELESS_KEY)
#   error "Can't use GlitchDebugger on 3DS without defining SSID, Wifi encryption mode and Wifi key in youn glf_config.h"
#endif

    GLF_NETINTEFRFACE_VERBOSE_PRINT("client: save network setting A.");
	result = nn::ac::DebugSetNetworkSetting1( 
                    GLF_CTR_DEBUG_WIRELESS_SSID, 
                    GLF_CTR_DEBUG_WIRELESS_SSID_LEN,
                    GLF_CTR_DEBUG_WIRELESS_ENC, 
                    GLF_CTR_DEBUG_WIRELESS_KEY, 
                    GLF_CTR_DEBUG_WIRELESS_KEY_LEN);
    result = nn::ac::CreateDefaultConfig( &config );
	if (result.IsFailure())
	{
		NN_UTIL_PANIC_IF_FAILED(nn::ac::Finalize());
	}

	nn::ac::DebugSetNetworkArea( &config, nn::ac::NETWORK_AREA_LAN );

	result = nn::ac::Connect( config );
	if( result.IsFailure() )
	{
		if (result==nn::ac::ResultLanConnected())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Successfully connected to a LAN.");
		}
		else if (result==nn::ac::ResultWanConnected())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Successfully connected to a WAN.");
		}
/*		
		else if (result==nn::fs::ResultNotInitialized().DESCRIPTION_TIMEOUT)
		{
			os::Printer::logf("The FS library has not been initialized.");
		}
*/
		else if (result==nn::ac::ResultNotFoundAccessPoint())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Unable to find an access point that could be connected to.");
		}
		else if (result==nn::ac::ResultCanceled())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Canceled while a connection was being processed.");
		}
		else if (result==nn::ac::ResultAlreadyExists())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Already processing.");
		}
		else if (result==nn::ac::ResultNotAgreeEula())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("The EULA has not been agreed to, or the icon file has not been configured.");
		}
		else if (result==nn::ac::ResultAlreadyConnectUnsupportAp())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Connecting to an unusable AP.");
		}
		else if (result==nn::ac::ResultWifiOff())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Cannot use a network because the system is in flight mode.");
		}
		else if (result==nn::ac::ResultFailedScan())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Failed scan.");
		}
		else if (result==nn::ac::ResultConflictIpAddress())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Conflicting IP address.");
		}
		else if (result==nn::ac::ResultFailedDhcp())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Name resolution failure.");
		}
		else if (result==nn::ac::ResultInvalidDns())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Name resolution failure.");
		}
		else if (result==nn::ac::ResultInvalidProxy())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Proxy failure.");
		}
		else if (result==nn::ac::ResultFailedConnTest())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Failed Internet connection test.");
		}
		else if (result==nn::ac::ResultFailedHotspotAuthentication())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Failed hotspot authentication.");
		}
		else if (result==nn::ac::ResultFailedHotspotConntest())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Passed hotspot authentication and then failed an Internet connection test.");
		}
		else if (result==nn::ac::ResultOutOfMemory())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Failed to allocate memory.");
		}
		else if (result==nn::ac::ResultUnsupportAuthAlgorithm())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Could not connect to an access point because it used an unsupported encryption scheme.");
		}
		else if (result==nn::ac::ResultDenyUsbAp())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Access to a Nintendo Wi-Fi USB Connector was denied.");
		}
		else if (result==nn::ac::ResultUnsupportPlace())
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("Could not connect because the network is unusable from the current location.");
		}
	}

	if (result.IsFailure())
	{
		GLF_NETINTEFRFACE_VERBOSE_PRINT("ko");
		NN_UTIL_PANIC_IF_FAILED(nn::ac::Finalize());
		return false;
	}

	GLF_NETINTEFRFACE_VERBOSE_PRINT("ok");
	const s32    sessionCount           = 1;
	const size_t bufferSizeForSockets   = 65536;
	const size_t workSizeForLibrary     = nn::socket::GetRequiredMemorySize(bufferSizeForSockets, sessionCount);
	u8* pWorkMemory = new u8[workSizeForLibrary + 4096];
	uptr workMemoryAddress = nn::math::RoundUp<uptr>(reinterpret_cast<uptr>(pWorkMemory), 4096);

	result = nn::socket::Initialize(workMemoryAddress, workSizeForLibrary, bufferSizeForSockets, sessionCount);
	NN_UTIL_PANIC_IF_FAILED(result);
    u8 macAddress[nn::socket::ETH_ALEN];
    nn::socket::GetMacAddress(macAddress);
    Console::Print("netinterface: "); 
    Console::Println("MAC address - %X:%X:%X:%X:%X:%X\n",
                     macAddress[0],
                     macAddress[1],
                     macAddress[2],
                     macAddress[3],
                     macAddress[4],
                     macAddress[5]);

	{
		s32 ret;
		nn::socket::InAddr addr, netmask;
		ret = nn::socket::GetPrimaryAddress(reinterpret_cast<u8*>(&addr), reinterpret_cast<u8*>(&netmask));
		GLF_ASSERT(ret == 0);
		Console::Println("host       : %s", nn::socket::InetNtoA(addr));
		GLF_NETINTEFRFACE_VERBOSE_PRINT("netmask    : %s", nn::socket::InetNtoA(netmask));

		nn::socket::InAddr dns1, dns2;
		ret = nn::socket::GetResolver(reinterpret_cast<u8*>(&dns1), reinterpret_cast<u8*>(&dns2));
		if (ret == 0)
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("dns1       : %s", nn::socket::InetNtoA(dns1));
			GLF_NETINTEFRFACE_VERBOSE_PRINT("dns2       : %s", nn::socket::InetNtoA(dns2));
		}

		nn::socket::InAddr gateway;
		ret = nn::socket::GetDefaultGateway(reinterpret_cast<u8*>(&gateway));
		if (ret == 0)
		{
			GLF_NETINTEFRFACE_VERBOSE_PRINT("gateway    : %s", nn::socket::InetNtoA(gateway));
		}

		GLF_NETINTEFRFACE_VERBOSE_PRINT("");
#ifndef NN_SWITCH_DISABLE_DEBUG_PRINT
		nn::socket::DumpRoutingTable();
#endif
		NN_UNUSED_VAR(ret);
	}

	m_ctrSocketLibIsInit=true;
	return true;
}
#endif


bool CNetInterfaceInit()
{
#ifdef _WIN32
	WORD version_requested = MAKEWORD(1, 1);
	WSADATA wsa;

	WSAStartup(version_requested, &wsa);

	if(wsa.wVersion != version_requested)
	{	
		// TODO log_error
		GLF_NETINTEFRFACE_ERROR_PRINT("Bad Winsock version %d\n", wsa.wVersion);
		return false;
	}
#elif defined(GLF_PLATFORM_CTR)	
	if(!initCTRDebug()){
        return false;
    }
#elif defined(GLF_PLATFORM_PS3)
	int ret = cellSysmoduleLoadModule(CELL_SYSMODULE_NET);
	if(ret < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("cellSysmoduleLoadModule(CELL_SYSMODULE_NET) failed (0x%x)\n", ret);
		return false;
	}

	ret = sys_net_initialize_network();
	if(ret < 0)
	{
		GLF_NETINTEFRFACE_ERROR_PRINT("sys_net_initialize_network() failed (0x%x)\n", ret);
		return false;
	}

	//ret = cellNetCtlInit();
	//if(ret < 0)
	//{
	//	Console::Println("cellNetCtlInit() failed. ret = 0x%x\n", ret);
	//	return ret;
	//}
#elif defined(GLF_PLATFORM_PSP2)
	SceNetInitParam param;
	param.memory = GlfAlloc(32 * 1024);
	param.size = 32 * 1024;
	param.flags = 0;
	int ret = sceNetInit(&param);
    GLF_ASSERT(ret == 0);
	ret = sceNetCtlInit();
    GLF_ASSERT(ret == 0);

	/*E libnetctl inet callback */
	//static int s_libnetctlInetCid;
	//ret = sceNetCtlInetRegisterCallback(libnetctlCallback, NULL, &s_libnetctlInetCid);

	SceNetId s = sceNetSocket("ping", SCE_NET_AF_INET, SCE_NET_SOCK_RAW, SCE_NET_IPPROTO_ICMP);
	int tppolicy = 0;
	ret = sceNetSetsockopt(s, SCE_NET_SOL_SOCKET, SCE_NET_SO_TPPOLICY, &tppolicy, sizeof(tppolicy));
    GLF_ASSERT(ret == 0);
	SceNetSockaddrIn addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_len = sizeof(addr);
	addr.sin_family = SCE_NET_AF_INET;
	addr.sin_port = 80;
	sceNetInetPton(SCE_NET_AF_INET, "10.137.38.1", &addr.sin_addr);
	addr.sin_port = htons(7940);
	char data[32] = {0};
	ret = sceNetSendto(s, (char*)data, sizeof(data), 0, (SceNetSockaddr*) &addr, sizeof(SceNetSockaddr));
    GLF_ASSERT(ret >= 0);

	int state = SCE_NET_CTL_STATE_DISCONNECTED;
	int64 expire = GetMilliseconds() + 9000;
	do
	{
		ret = sceNetCtlInetGetState(&state);
        GLF_ASSERT(ret == 0);
		//printf("state=%d", state);
		//sceNetShowIfconfig();
		sceNetCtlCheckCallback();
		Thread::Sleep(100);
	} while(GetMilliseconds() < expire && state != SCE_NET_CTL_STATE_IPOBTAINED);
#endif
	return true;
}

} //namespace debugger
} //namespace glf

#endif // GLF_ENABLE_DEBUGGER
